﻿using System.Collections.Generic;
using CubeCube.Utils;
using CubeCube.World.Generator;
using System.Diagnostics;
using System.Threading;
using System.IO;

namespace CubeCube.Chunk.Strategies
{
    //保证
    class BasicChunkStrategies : IVisibleChunksStrategies
    {
        //正在构建的chunk
        private List<Chunk> buidingChunk = new List<Chunk>();
        private List<Chunk> readyChunk = new List<Chunk>();

        public GeneratorProcess Genrator(ChunkManager chunkManager)
        {
            IndexPosition chunkWorldPos = new IndexPosition(0, 0, 0);
            Chunk chunk = chunkManager.CreateChunk(chunkWorldPos);
            chunk.terrainName = GetChunkTerrain(chunkManager, chunk.tileOffset);
            CubeWorldGenerator generator = chunkManager.generatorConfig.generators[chunk.terrainName];
            generator = chunk.Generate(generator);
            return new GeneratorProcess(generator, chunk);
        }

        public void Update(ChunkManager chunkManager, List<Chunk> chunks, List<Chunk> cachechunks)
        {
            lock(readyChunk)
            {
                chunkManager.cachechunks.AddRange(readyChunk);
                readyChunk.Clear();
            }

            IndexPosition playerWorldPos = Graphics.Vector3ToTileIndexPosition(chunkManager.world.PlayerPosition);
            IndexPosition chunkPos = Graphics.TilePositionToChunkPosition(playerWorldPos);
            int id = GetAreaID(chunkManager, playerWorldPos);
            int idX = id & 3, idZ = id >> 2;

            //生成
            if (idX < 2) //left
            {
                CacheNewChunck(chunkManager, chunkPos + Graphics.FACE_DIR_VECTOR[(int)Graphics.Faces.Left] * Graphics.CHUNK_SIZE);
            }
            else  //right
            {
                CacheNewChunck(chunkManager, chunkPos + Graphics.FACE_DIR_VECTOR[(int)Graphics.Faces.Right] * Graphics.CHUNK_SIZE);
            }
            if (idZ < 2)  //back
            {
                CacheNewChunck(chunkManager, chunkPos + Graphics.FACE_DIR_VECTOR[(int)Graphics.Faces.Back] * Graphics.CHUNK_SIZE);
            }
            else  //front
            {
                CacheNewChunck(chunkManager, chunkPos + Graphics.FACE_DIR_VECTOR[(int)Graphics.Faces.Front] * Graphics.CHUNK_SIZE);
            }

            //销毁

        }


        private GeneratorProcess GeneratorChunk(ChunkManager chunkManager, Chunk chunk)
        {
            //获取
            string terrainName = GetChunkTerrain(chunkManager, chunk.tileOffset);
            chunk.terrainName = terrainName;
            return new GeneratorProcess(chunk.Generate(chunkManager.generatorConfig.generators[terrainName]), chunk);
        }

        private string GetChunkTerrain(ChunkManager chunkManager, IndexPosition worldPos)
        {
            Dictionary<string, int> nearTerrain = new Dictionary<string, int>();
            int[] dir = { 1, 0, -1, 0, 0, 1, 0, -1 };
            int count = 0, range = 0;
            for(int i = 0; i < 8; i += 2)
            {
                Chunk chunk = chunkManager.GetChunk(new IndexPosition(worldPos.x + dir[i], worldPos.y, worldPos.z + dir[i + 1]));
                if(chunk == null)
                {
                    List<KeyValuePair<int, string>> near = chunkManager.generatorConfig.neighborChunk[chunk.terrainName];
                    for(int j = 0; j < near.Count; ++j)
                    {
                        nearTerrain[near[j].Value] += near[j].Key;
                        range += near[j].Key;
                    }
                    count++;
                }
            }
            if(count > 0)
            {
                int rand = chunkManager.world.randomGenerator.Next(range);
                int cur = 0;
                Dictionary<string, int>.Enumerator iter = nearTerrain.GetEnumerator();
                while (iter.MoveNext())
                {
                    cur += iter.Current.Value;
                    if(rand < cur)
                    {
                        return iter.Current.Key;
                    }
                }
                iter.Dispose();
            }
            Debug.Assert(false);
            return "";
        }

        

        private int GetAreaID(ChunkManager chunkManager, IndexPosition playerWorldPos)
        {
            IndexPosition chunkPos = Graphics.TilePositionToChunkPosition(playerWorldPos);
            IndexPosition offset = playerWorldPos - chunkPos;
            int x = offset.x / (Graphics.CHUNK_SIZE >> 2);
            int z = offset.z / (Graphics.CHUNK_SIZE >> 2);
            return z * 4 + x;
        }


        private void CacheNewChunck(ChunkManager chunkManager, IndexPosition worldPos)
        {
            //先判断当前是否存在此chunk
            for(int i = 0; i < chunkManager.chunks.Count; ++i)
            {
                if(chunkManager.chunks[i].tileOffset == worldPos)
                {
                    return;
                }
            }

            for(int i = 0; i < chunkManager.cachechunks.Count; ++i)
            {
                if(chunkManager.cachechunks[i].tileOffset == worldPos)
                {
                    chunkManager.chunks.Add(chunkManager.cachechunks[i]);
                    chunkManager.cachechunks.RemoveAt(i);
                    //TODO: 上报
                    //chunkManager.上报此chunk复活,进行相关临界更新
                    return;
                }
            }
            
            lock (buidingChunk)
            {
                for (int i = 0; i < buidingChunk.Count; ++i)
                {
                    if (buidingChunk[i].tileOffset == worldPos)
                    {
                        //正在构建
                        return;
                    }
                }
            }

            Chunk chunk = chunkManager.CreateChunk(worldPos);
            lock(buidingChunk)
            {
                buidingChunk.Add(chunk);
            }
            string filename = chunkManager.CHUNK_FILE_PATH + Path.DirectorySeparatorChar + chunk.ToString();
            if (chunkManager.world.fileOperator.Exist(filename))
            {
                ThreadStart threadStart = new ThreadStart(delegate ()
                {
                    BinaryReader br = chunkManager.world.fileOperator.ReadBinary(filename);
                    chunk.Load(br);
                    lock (buidingChunk)
                    {
                        buidingChunk.Remove(chunk);
                    }
                    lock (readyChunk)
                    {
                        readyChunk.Add(chunk);
                    }
                });
                Thread th = new Thread(threadStart);
                th.Start();
            }
            else
            {
                GeneratorProcess progress = GeneratorChunk(chunkManager, chunk);
                if(progress != null)
                {
                    ThreadStart threadStart = new ThreadStart(delegate ()
                    {
                        while (!progress.IsFinished())
                        {
                            progress.Generate();
                        }
                        lock(buidingChunk)
                        {
                            buidingChunk.Remove(chunk);
                        }
                        lock(readyChunk)
                        {
                            readyChunk.Add(chunk);
                        }
                    });
                    Thread th = new Thread(threadStart);
                    th.Start();
                }
            }
        }
    }
}
